package org.oschina.api.discuss.discuss.dao;

import java.util.Iterator;
import java.util.List;
import org.oschina.Constants;
import org.oschina.api.BaseDAO;
import org.oschina.api.comment.dao.CommentDAO;
import org.oschina.api.discuss.discuss.model.Discuss;
import org.oschina.api.discuss.discuss.vo.DiscussIndexDataVo;
import org.oschina.api.discuss.discuss.vo.DiscussIndexVo;
import org.oschina.api.discuss.discuss.vo.DiscussVo;
import org.oschina.api.discuss.forum.DiscussForumIndexType;
import org.oschina.api.discuss.forum.dao.DiscussForumDAO;
import org.oschina.api.discuss.forum.vo.DiscussForumVo;
import org.oschina.api.index.model.IndexHandleBean;
import org.oschina.api.user.user.dao.UserDAO;
import org.oschina.api.user.user.vo.UserVo;
import org.oschina.helper.index.IndexHelper;
import org.oschina.helper.index.IndexResultBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

@Repository
public class DiscussDAO extends BaseDAO {

	@Autowired
	private IndexHelper indexHelper;
	@Autowired
	private CommentDAO commentDAO;
	@Autowired
	private DiscussForumDAO discussForumDAO;
	@Autowired
	private UserDAO userDAO;
	
	private final String findVo="SELECT dc.id,dc.title,dc.content,us_1.id as 'author.id',us_1.username as 'author.username',us_2.id as 'last_author.id',us_2.username as 'last_author.username', " +
			"dc.comment_id,dc.views_count,dc.comment_count,dc.favorite_count,dc.create_time,dc.type,dc.forum as 'forum.id',dc.last_time,ord.id as 'order.id',ord.name as 'order.name',ord.color as 'order.color',ord.font as 'order.font' FROM "+
			Constants.DB_DISCUSS+" dc LEFT JOIN "+Constants.DB_DISCUSS_ORDER+" ord ON ord.id=dc.order AND ord._default=false,"+Constants.DB_USER+" us_1,"+Constants.DB_USER+" us_2 WHERE " +
			"us_1.id=dc.author_id AND us_2.id=dc.last_author_id";
	
	private final String findVoByForumAndType=findVo+" AND dc.forum=? AND dc.type=? ORDER BY case when ord.`index` is null then 1 else 0 end, ord.`index`,dc.last_time DESC LIMIT ?,?";
	/**
	 * 根据板块和类型返回
	 * @param forum
	 * @param type
	 * @param first
	 * @param max
	 * @return
	 */
	public List<DiscussVo> findVoByForumAndType(int forum,int type,int first,int max)
	{
		if(first!=0)
			return getQueryHelper().query(findVoByForumAndType, new Object[]{forum,type,first,max}, DiscussVo.class);
		
		List<DiscussVo> result=getQueryHelper().query_cache(findVoByForumAndType, new Object[]{forum,type,first,max},DiscussVo.class);
		
		Iterator<DiscussVo> iterator=result.iterator();
		while(iterator.hasNext())
		{
			DiscussVo discussVo=iterator.next();
			if(!discussVo.getForum().getId().equals(forum) || !discussVo.getType().equals(type))
				iterator.remove();
		}
		return result;
	}
	
	/**
	 * 根据板块和类型返回数量
	 * @param forum
	 * @param type
	 * @return
	 */
	public int getCountByforumAndType(int forum,int type)
	{
		return (int) getQueryHelper().queryDataSum_cache(new String[]{"forum=?","type=?"}, 
				new Object[]{forum,type},DiscussVo.class);
	}
	
	private final String findVoByType=findVo+" AND dc.type=? ORDER BY ord.`index` DESC,dc.last_time DESC LIMIT ?,?";
	/**
	 * 根据类型返回
	 * @param type
	 * @param first
	 * @param max
	 * @return
	 */
	public List<DiscussVo> findVoByType(int type,int first,int max)
	{
		return getQueryHelper().query(findVoByType, new Object[]{type,first,max}, DiscussVo.class);
	}
	
	private final String findVoByTagAndType="SELECT dc.id,dc.title,dc.content,us_1.id as 'author.id',us_1.username as 'author.username',us_2.id as 'last_author.id',us_2.username as 'last_author.username', " +
		"dc.comment_id,dc.views_count,dc.comment_count,dc.favorite_count,dc.create_time,dc.type,dc.forum as 'forum.id',dc.last_time,ord.name as 'order.name',ord.color as 'order.color',ord.font as 'order.font' FROM "+
		Constants.DB_DISCUSS+" dc LEFT JOIN "+Constants.DB_DISCUSS_ORDER+" ord ON ord.id=dc.order AND ord._default=false,"+Constants.DB_USER+" us_1,"+Constants.DB_USER+" us_2,"+Constants.DB_DISCUSS_TAG+" dct WHERE " +
		"us_1.id=dc.author_id AND us_2.id=dc.last_author_id AND dct.discuss=dc.id AND dct.content=? AND dc.type=? ORDER BY case when ord.`index` is null then 1 else 0 end, ord.`index`,dc.last_time DESC LIMIT ?,?";
	/**
	 * 根据标签和类型返回
	 * @param tag
	 * @param type
	 * @param first
	 * @param max
	 * @return
	 */
	public List<DiscussVo> findVoByTagAndType(String tag,int type,int first,int max)
	{
		if(isNeedCacheByTag(tag) && first==0)
		{
			List<DiscussVo> result=getQueryHelper().query_cache(Constants.CACHE_DISCUSS,Constants.TIMEOUT_DISCUSS,findVoByTagAndType, new Object[]{tag,type,first,max}, DiscussVo.class);
			
			Iterator<DiscussVo> iterator=result.iterator();
			while(iterator.hasNext())
			{
				DiscussVo discussVo=iterator.next();
				if(!discussVo.getType().equals(type))
					iterator.remove();
			}
			
			return result;
		}
		
		return getQueryHelper().query(findVoByTagAndType, new Object[]{tag,type,first,max}, DiscussVo.class);
	}
	
	private final String getCountByTagAndType="SELECT COUNT(*) FROM "+Constants.DB_DISCUSS+" dc,"+Constants.DB_DISCUSS_TAG+" dct WHERE dct.discuss=dc.id AND dc.type=? AND dct.content=?";
	/**
	 * 根据标签和类型返回数量
	 * @param tag
	 * @param type
	 * @return
	 */
	public int getCountByTagAndType(String tag,int type)
	{
		if(isNeedCacheByTag(tag))
			return (Integer) getQueryHelper().queryForObject_cache(Constants.CACHE_DISCUSS,Constants.TIMEOUT_DISCUSS, getCountByTagAndType,new Object[]{type,tag}, Integer.class);
		
		return (Integer) getQueryHelper().queryForObject(getCountByTagAndType, new Object[]{type,tag}, Integer.class);
	}
	/**
	 * 需要缓存的标签
	 * @param tag
	 */
	public void needCacheByTag(String tag)
	{
		getCacheHelper().hset(Constants.CACHE_DISCUSS_BY_TAG, tag, true, Constants.TIMEOUT_DISCUSS);
	}
	/**
	 * 是否需要缓存
	 * @param tag
	 * @return
	 */
	private boolean isNeedCacheByTag(String tag)
	{
		return getCacheHelper().hexists(Constants.CACHE_DISCUSS_BY_TAG, tag);
	}
	
	/**
	 * 根据类型返回数量
	 * @param type
	 * @return
	 */
	public int getCountByType(int type)
	{
		return (int) getQueryHelper().queryDataSum(new String[]{"type=?"}, 
				new Object[]{type},DiscussVo.class);
	}
	
	/**
	 * 根据ID返回
	 * @param id
	 * @return
	 */
	private final String findById=findVo+" AND dc.id=?";
	public DiscussVo findVoById(int id)
	{
		DiscussVo cache=_findVoByCache(id);
		if(cache!=null)
		{
			cacheData(cache);
			return cache;
		}
		
		return (DiscussVo) getQueryHelper().queryForObject(findById,new Object[]{id},DiscussVo.class);
	}
	
	private DiscussVo _findVoByCache(int id)
	{
		return (DiscussVo) getCacheHelper().hget(getIdKey(id), DiscussVo.class);
	}
	
	/**
	 * 根据类型搜索
	 * @param keywords
	 * @param type
	 * @param first
	 * @param max
	 * @return
	 */
	public IndexResultBean<DiscussVo> searchVoByType(String keywords[],int type,int first,int max)
	{
		StringBuilder where=null;
		for(String keyword:keywords)
		{
			if(where==null)
				where=new StringBuilder();
			else
				where.append(" OR ");
			
			where.append("(title:'");
			where.append(keyword);
			where.append("' OR content:'");
			where.append(keyword);
			where.append("')");
		}
		where.append(" AND type:");
		where.append(type);
		return indexHelper.query(Constants.INDEX_PATH_DISCUSS, where.toString(), new String[]{"create_time"}, new String[]{"desc"}, first, max, DiscussVo.class);
	}

	/**
	 * 返回相关讨论
	 * @param discuss
	 * @param first
	 * @param max
	 * @return
	 */
	public List<DiscussVo> findByRelated(DiscussVo discuss,int first,int max)
	{
		if(!isInCache(discuss.getId()))
			return _findByRelated(discuss,first,max);
		
		String key=this.getKey(Constants.CACHE_DISCUSS_RELATED, discuss.getId());
		List<DiscussVo> result=(List<DiscussVo>) getCacheHelper().get(key, List.class);
		if(result==null)
		{
			result=_findByRelated(discuss,first,max);
			getCacheHelper().set(key, result, Constants.TIMEOUT_DISCUSS_RELATED);
		}
		return result;
	}
	private List<DiscussVo> _findByRelated(DiscussVo discuss,int first,int max)
	{
		return searchVoByType(new String[]{discuss.getTitle()}, discuss.getType(), first, max).getResult();
	}
	
	/**
	 * 返回首页数据
	 * @param planePageCount
	 * @param verticalPageCount
	 * @param type
	 * @return
	 */
	public DiscussIndexDataVo getIndexData(int planePageCount,int verticalPageCount,int type)
	{
		DiscussIndexDataVo discussIndexDataVo=(DiscussIndexDataVo) getCacheHelper().get(Constants.CACHE_DISCUSS_INDEX,DiscussIndexDataVo.class);
		
		if(discussIndexDataVo==null)
		{
			discussIndexDataVo=new DiscussIndexDataVo();
			List<DiscussForumVo> plane=discussForumDAO.findByIndexType(DiscussForumIndexType.PLANE);
			List<DiscussForumVo> vertical=discussForumDAO.findByIndexType(DiscussForumIndexType.VERTICAL);
			
			for(DiscussForumVo discussForumVo:plane)
			{
				DiscussIndexVo discussIndexVo=new DiscussIndexVo();
				
				discussForumVo.setCheck(null);
				discussForumVo.setCreate_time(null);
				discussForumVo.setIndex(null);
				discussForumVo.setIndex_type(null);
				
				discussIndexVo.setForum(discussForumVo);
				discussIndexVo.setDiscuss(findVoByForumAndType(discussForumVo.getId(), type, 0, planePageCount));
				discussIndexDataVo.getPlane().add(discussIndexVo);
			}
			
			for(DiscussForumVo discussForumVo:vertical)
			{
				DiscussIndexVo discussIndexVo=new DiscussIndexVo();
				
				discussForumVo.setCheck(null);
				discussForumVo.setCreate_time(null);
				discussForumVo.setIndex(null);
				discussForumVo.setIndex_type(null);
				
				discussIndexVo.setForum(discussForumVo);
				discussIndexVo.setDiscuss(findVoByForumAndType(discussForumVo.getId(), type, 0, verticalPageCount));
				discussIndexDataVo.getVertical().add(discussIndexVo);
			}
			
			getCacheHelper().set(Constants.CACHE_DISCUSS_INDEX, discussIndexDataVo, Constants.TIMEOUT_DISCUSS_INDEX);
		}
		
		return discussIndexDataVo;
	}
	
	/**
	 * 创建
	 * @param discuss
	 * @return
	 */
	public Discuss create(Discuss discuss)
	{
		createIndex(new String[]{"id","title","content","type","create_time"}, new Object[]{discuss.getId(),discuss.getTitle(),discuss.getContent(),discuss.getType(),discuss.getCreate_time()});
		
		getQueryHelper().insert(new String[]{"id","title","content","author_id","author_ip","comment_id","views_count","comment_count","favorite_count","type","forum","`order`","create_time","last_author_ip","last_author_id","last_time"}, 
				new Object[]{discuss.getId(),discuss.getTitle(),discuss.getContent(),discuss.getAuthor(),discuss.getAuthor_ip(),discuss.getComment_id(),discuss.getViews_count(),
				discuss.getComment_count(),discuss.getFavorite_count(),discuss.getType(),discuss.getForum(),discuss.getOrder(),discuss.getCreate_time(),discuss.getLast_author_ip(),discuss.getLast_author(),discuss.getLast_time()},DiscussVo.class);
		return discuss;
	}
	
	/**
	 * 更新类型
	 * @param id
	 * @param type
	 * @return
	 */
	public int updateType(int id,int type)
	{
		updateIndexById(id,new String[]{"type"}, new Object[]{type});
		updateCache(id,new String[]{"type"},new Object[]{type});
		return (int) getQueryHelper().update(new String[]{"type=?"}, new String[]{"id=?"}, new Object[]{type,id},DiscussVo.class);
	}

	/**
	 * 更新最后回复时间和作者
	 * @param id
	 * @param lastAuthor
	 * @param lastIp
	 * @param lastTime
	 */
	public void updateLastTimeAndLastAuthor(int id,UserVo lastAuthor,String lastIp,long lastTime)
	{
		updateCache(id,new String[]{"last_author","last_time"},new Object[]{lastAuthor,lastTime});
		batchUpdate_queue("UPDATE "+Constants.DB_DISCUSS+" SET last_author_ip=?,last_time=?,last_author_id=? WHERE id=?", new Object[]{lastIp,lastTime,lastAuthor,id});
	}
	
	/**
	 * 更新浏览量
	 * @param id
	 * @param count
	 */
	public void updateViewsCount(int id,int count)
	{
		getCacheHelper().hincr(getIdKey(id), "views_count", count);
		batchUpdate_queue("UPDATE "+Constants.DB_DISCUSS+" SET views_count=views_count+? WHERE id=?", new Object[]{count,id});
	}
	
	/**
	 * 更新评论数量
	 * @param id
	 * @param count
	 */
	public void updateCommentCount(int id,int count)
	{
		getCacheHelper().hincr(getIdKey(id), "comment_count", count);
		batchUpdate_queue("UPDATE "+Constants.DB_DISCUSS+" SET comment_count=comment_count+? WHERE id=?", new Object[]{count,id});
	}
	
	/**
	 * 更新关注数量
	 * @param id
	 * @param count
	 */
	public void updateFavoriteCount(int id,int count)
	{
		getCacheHelper().hincr(getIdKey(id), "favorite_count", count);
		batchUpdate_queue("UPDATE "+Constants.DB_DISCUSS+" SET favorite_count=favorite_count+? WHERE id=?", new Object[]{count,id});
	}

	/**
	 * 更新板块
	 * @param id
	 * @param forum
	 * @return
	 */
	public int updateForum(int id,int forum)
	{
		if(isInCache(id))
			updateCache(id,new String[]{"forum"},new Object[]{forum});
		return (int) getQueryHelper().update(new String[]{"forum=?"}, new String[]{"id=?"}, new Object[]{forum,id},DiscussVo.class);
	}
	
	/**
	 * 编辑
	 * @param software
	 * @return
	 */
	public int edit(Discuss discuss)
	{
		updateIndexById(discuss.getId(),new String[]{"title","content"}, new Object[]{discuss.getTitle(),discuss.getContent()});
		updateCache(discuss.getId(),new String[]{"title","content"},new Object[]{discuss.getTitle(),discuss.getContent()});
		return (int) getQueryHelper().update(new String[]{"title=?","content=?"},new String[]{"id=?"},new Object[]{discuss.getTitle(),discuss.getContent(),discuss.getId()},DiscussVo.class);
	}
	
	/**
	 * 更新排序
	 * @param id
	 * @param order
	 * @return
	 */
	public int updateOrder(int id,int order)
	{
		return (int) getQueryHelper().update(new String[]{"`order`=?"}, new String[]{"id=?"}, new Object[]{order,id},DiscussVo.class);
	}
	
	private boolean isInCache(int id)
	{
		return getCacheHelper().exists(getIdKey(id));
	}
	
	private void updateCache(int id,String fields[],Object values[])
	{
		updateCache(Constants.CACHE_DISCUSS, id, fields, values, Constants.TIMEOUT_DISCUSS);
	}
	
	/**
	 * 通知缓存需要的数据
	 * @param id
	 */
	private void cacheData(DiscussVo discussVo)
	{
		commentDAO.needCache(discussVo.getComment_id());
	}
	
	private String getIdKey(Object id)
	{
		return getKey(Constants.CACHE_DISCUSS,id);
	}
	
	private void updateIndexById(int id,String fields[],Object values[])
	{
		indexHandle(new IndexHandleBean(Constants.INDEX_PATH_DISCUSS,"id:"+id,fields, values));
	}
	
	private void createIndex(String fields[],Object values[])
	{
		indexHandle(new IndexHandleBean(Constants.INDEX_PATH_DISCUSS,fields, values));
	}
	
	private void indexHandle(IndexHandleBean indexHandleBean)
	{
		getMessageQueueHelper().send(Constants.QUEUE_INDEX_HANDLE,indexHandleBean);
	}
}